package server.rest;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;



class HandlerMap {

	class Element
	{
		HashMap<String,Element> elements;
		Handler handler;
		String name;
		List<String> parameters;
		
		public Element(String name)
        {
        	this.name=name;
         	this.elements=new HashMap<String, HandlerMap.Element>();
        }
	}
        HashMap<String, HashMap<String, Element>> Methods;
        
		public HandlerMap()
        {
        	init(new String[]{"GET","PUT","POST","DELETE","OPTIONS"});
        }
        public HandlerMap(String[] methods)
        {
        	init(methods);
        }
        void init(String[] methods)
        {
        	Methods=new HashMap<String, HashMap<String,Element>>();
        	for (String method:methods)
        	{
        		Methods.put(method, new HashMap<String, HandlerMap.Element>() );
        	}
        }
        public class Result
        {
			HashMap<String,String> parameters;
        	Handler handler;
        	public Result(HashMap<String,String> parameters,Handler handler)
        	{
        		this.parameters=parameters;
        		this.handler=handler;
        	}
			public Handler getHandler() {
				return this.handler;
			}
        	public HashMap<String, String> getParameters() {
				return parameters;
			}
        }
        
        public Result find(String method,String path )
        {
            HashMap<String, Element> elements=Methods.get(method);
            if (elements == null)
            {
                return null;
            }

            String[] pieces = path.split("/|\\.");
            Element element=null;
            ArrayList<String> parameterList=new ArrayList<String>(pieces.length);
            for (int i = 1; i < pieces.length; i++)
            {
                String piece = pieces[i];
                element=elements.get(piece);
                if (element == null)
                {
                	element=elements.get("");
                	if (element!=null)
                    {
                        parameterList.add(piece);
                    }
                    else
                    {
                        return null;
                    }
                }
                elements = element.elements;
            }
            if (element==null)
            {
            	return null;
            }
            if (element.handler==null)
            {
            	return null;
            }
            HashMap<String, String> parameters = new HashMap<String, String>();
            for (int i=0;i<element.parameters.size();i++)
            {
                parameters.put(element.parameters.get(i),parameterList.get(i));
            }
            return new Result(parameters,element.handler);
        }
        public void register(Handler handler) throws Exception
        {
            HashMap<String, Element> elements = Methods.get(handler.getHandlerConfig().getMethod());
            String schema = handler.getBasePathSchema();
            int index = schema.indexOf('?');
            if (index >= 0)
            {
                schema = schema.substring(0, index);
            }
            if (schema.length() == 0)
            {
                throw new Exception("Invalid schema: empty");
            }
            if (schema.charAt(0)!='/')
            {
                throw new Exception("Invalid schema: schema must start with / character. Schema="+schema);
            }
            String[] pieces = schema.split("/|\\.");
            Element element = null;
            ArrayList<String> parameters=new ArrayList<String>(pieces.length);
            for (int i=1;i<pieces.length;i++)
            {
                String piece = pieces[i];
                if (piece.length() == 0)
                {
                    throw new Exception("Invalid schema: // found. Schema="+schema);
                }
                String parameter = null;
                if (piece.charAt(0) == '(')
                {
                    if (piece.charAt(piece.length() - 1) != ')')
                    {
                        throw new Exception("Invalid schema: open ( found without matching close. Schema="+schema);
                    }
                    if (piece.length()<3)
                    {
                        throw new Exception("Invalid schema: empty parameter found.");
                    }
                    parameter = piece.substring(1, piece.length() - 1);
                    if (parameters.contains(parameter))
                    {
                        throw new Exception("Invalid schema: parameter name "+parameter+" is already defined. Schema="+schema);
                    }
                    parameters.add(parameter);
                    piece = "";
                }
                else if (piece.charAt(piece.length() - 1) == ')')
                {
                    throw new Exception("Invalid schema: close ) found without matching open. Schema="+schema);
                }

                element=elements.get(piece);
                if (element==null)
                {
                    element = new Element(piece);
                    elements.put(piece, element);
                }
                elements = element.elements;
            }
            if (element == null)
            {
                throw new Exception("Invalid schema. No names present.");
            }
            if (element.handler != null && !HttpMethod.OPTIONS.toString().equals(element.handler.method))
            {
                throw new Exception("Schema already registered: " + handler.getHandlerConfig().getMethod() + " "
                                + handler.getHandlerConfig().getSchema() + " registered to "
                                + element.handler.getObject().getClass().getName() + "."
                                + element.handler.getMethod().getName());
            }
            else
            {
                element.handler = handler;
                element.parameters = parameters;
            }
        }

        
	}

